﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Threading;
using GenericSimulator;
using VEGA.Algorithm.Elements;

namespace VEGA.Algorithm
{
    public class Vega
    {
        #region Properties

        public List<Individual> CurrentPopulation { get; private set; }
        public List<Individual> LastEvaluated { get; private set; }

        public int PopulationSize
        {
            get { return _populationSize; }
            set { _populationSize = value; }
        }

        public int LastGeneration
        {
            get { return _currentGeneration - 1; }
        }

        public int ToDoGeneration
        {
            get { return _currentGeneration + 1; }
        }

        public int CurrentGeneration
        {
            get { return _currentGeneration; }
        }

        public int RunStepCount
        {
            get { return _runStepCount; }
            set { _runStepCount = value; }
        }

        [Obsolete("This Property is not implemented")]
        public int ParentGametsCount
        {
            get { return _parentGametsCount; }
            set { _parentGametsCount = value; }
        }

        #endregion

        #region Events

        public event OnCicleDoneHandler OnClicleDone;

        #endregion

        #region Delegates

        public delegate void OnCicleDoneHandler(Vega sender, VegaArgs e);

        #endregion

        #region Members

        private Evaluator _evaluator;
        private Selector _selector;
        private Crosser _crosser;
        private ISimulator _simulator;

        private int _populationSize = 10;
        private int _threadCount = 2;
        private int _currentGeneration = 0;
        private int _runStepCount;
        private int _parentGametsCount = 2;

        #endregion

        #region Constructors

        public Vega()
        {
        }

        #endregion

        #region Public Methods

        public void Initialize(ISimulator simulator)
        {
            Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture;
            _simulator = simulator;
            _evaluator = new Evaluator();
            _selector = new Selector();
            _crosser = new Crosser();
            CurrentPopulation = GetPopulationFromConfigurations(_simulator.GetInitialConfigurations(PopulationSize));
        }

        private List<Individual> GetPopulationFromConfigurations(IEnumerable<IConfiguration> configurations)
        {
            return configurations.Select(configuration => new Individual(configuration)).ToList();
        }

        public void Run(int generations)
        {
            for (var i = 0; i < generations; i++)
            {
                Step();
            }
        }

        public void Step()
        {
            var simulatedPopulation = DoSimulation(CurrentPopulation);

            var evaluatedPopulation = DoEvaluation(simulatedPopulation);

            LastEvaluated = new List<Individual>(evaluatedPopulation);

            var selectedPopulation = DoSelection(evaluatedPopulation);

            var crossoveredPopulation = DoCrossover(selectedPopulation);

            var mutatedPopulation = DoMutation(crossoveredPopulation);

            CurrentPopulation = new List<Individual>(mutatedPopulation);

            _currentGeneration++;

            var args = new VegaArgs(PopulationSize, CurrentGeneration,
                new List<Individual>(CurrentPopulation), new List<Individual>(LastEvaluated));
            if (OnClicleDone != null)
                OnClicleDone(this, args);
        }

        #endregion

        #region Private Methods

        private IEnumerable<Individual> DoSimulation(IEnumerable<Individual> population)
        {
            _simulator.Input = GetSimulatorInput(population);
            _simulator.Run();
            return GetSimulatedPopulation(_simulator.Output);
        }

        private IEnumerable<Individual> GetSimulatedPopulation(IResult output)
        {
            var dictionary = output.GetDataDictionary();
            var resultPopulation = new List<Individual>();

            foreach (var pair in dictionary)
            {
                var split = pair.Value.Split(new[] {'\\'});
                var config = _simulator.GetEmptyConfigurationInstance(pair.Key);
                config.FromString(split[4]);

                var individual = new Individual(config);
                individual.Ipc = double.Parse(split[1]);
                individual.Power = double.Parse(split[2]);

                resultPopulation.Add(individual);
            }

            return resultPopulation;
        }

        private List<IConfiguration> GetSimulatorInput(IEnumerable<Individual> population)
        {
            return population.Select(individual => individual.Configuration).ToList();
        }

        private List<Individual> DoEvaluation(IEnumerable<Individual> simulatedPopulation)
        {
            _evaluator.Input = new List<Individual>(simulatedPopulation);
            _evaluator.Run();
            return _evaluator.Output;
        }

        private IEnumerable<Individual> DoSelection(IEnumerable<Individual> evaluatedPopulation)
        {
            _selector.Input = new List<Individual>(evaluatedPopulation);
            _selector.SelectionSize = _populationSize/2;

            _selector.Run();

            return _selector.Output;
        }

        private IEnumerable<Individual> DoCrossover(IEnumerable<Individual> selectedPopulation)
        {
            _crosser.Input = new List<Individual>(selectedPopulation);
            _crosser.Run(_simulator);

            return _crosser.Output;
        }

        private IEnumerable<Individual> DoMutation(IEnumerable<Individual> children)
        {
            var inChildren = new List<Individual>(children);
            foreach (var individual in inChildren)
            {
                _simulator.RandomizeConfiguration(individual.Configuration, 1);
            }
            return new List<Individual>(inChildren);
        }

        #endregion
    }
}